package kbl.language;

import java.util.Arrays;
import java.util.Iterator;
import java.util.TreeSet;

public class CharacterClass implements Comparable<CharacterClass> {
	private TreeSet<CharRange> cc = new TreeSet<CharRange>();

	public TreeSet<CharRange> getCharRanges() {
		return this.cc;
	}

	boolean contains(char c) {
		return cc.contains(new CharRange(c, c));
	}

	boolean contains(CharRange cr) {
		return cc.contains(cr);
	}

	boolean addRange(CharRange cr) {
		// Error happen when size > 0 on last();????
		/*
		 * CharRange t1 = new CharRange((char) (cr.low - 1), (char) (cr.low -
		 * 1)); CharRange t2 = new CharRange((char) (cr.high + 1), (char)
		 * (cr.high + 1)); TreeSet<CharRange> t3 = (TreeSet<CharRange>)
		 * cc.subSet(t1, true, t2, true); if (t3.size() > 0) { if
		 * (t3.last().high > cr.high){ cr.high = t3.last().high; } if
		 * (t3.first().low < cr.low){ cr.low = t3.first().low; }
		 * cc.removeAll(t3); } return cc.add(cr);
		 */

		char low = (char) Math.max(Character.MIN_VALUE, cr.low - 1);
		char high = (char) Math.min(Character.MAX_VALUE, cr.high + 1);
		CharRange crfinal = new CharRange(cr.low, cr.high);
		CharRange rlow = new CharRange(low, low);
		CharRange rhigh = new CharRange(high, high);
		// Look for a range abutting low on the left.
		if (cr.low > Character.MIN_VALUE) {
			TreeSet<CharRange> ts = (TreeSet<CharRange>) cc.headSet(rlow, true);
			if (ts.size() > 0) {
				CharRange cmp = ts.last();
				if (cmp.low <= low && low <= cmp.high) {
					crfinal.low = cmp.low;
					if (cmp.high > cr.high) {
						crfinal.high = cmp.high;
					}
					cc.remove(cmp);
				}
			}
		}

		// Look for a range abutting hi on the right.
		if (cr.high < Character.MAX_VALUE) {
			TreeSet<CharRange> ts = (TreeSet<CharRange>) cc
					.tailSet(rhigh, true);
			if (ts.size() > 0) {
				CharRange cmp = ts.first();
				if (cmp.low <= high && high <= cmp.high) {
					crfinal.high = cmp.high;
					cc.remove(cmp);
				}
			}
		}

		// Look for ranges between low and high. Take them out.
		cc.removeAll(cc.subSet(rlow, rhigh));
		return cc.add(crfinal);
	}

	boolean breakRange(CharRange cr) {
		CharRange rlow = new CharRange(cr.low, cr.low);
		CharRange rhigh = new CharRange(cr.high, cr.high);
		@SuppressWarnings("unchecked")
		TreeSet<CharRange> breakRange = (TreeSet<CharRange>) ((TreeSet<CharRange>) (cc
				.subSet(rlow, true, rhigh, true))).clone();
		if (breakRange == null) {
			return cc.add(cr);
		}
		cc.removeAll(breakRange);

		Iterator<CharRange> iter = breakRange.iterator();
		char tlow = cr.low, thigh = cr.high;
		while (iter.hasNext()) {
			CharRange cmp = iter.next();
			if (cmp.low > thigh || tlow > cmp.high) {
				throw new RuntimeException("No overlap");
			} else {
				int[] a = { cmp.low, cmp.high, tlow, thigh };
				Arrays.sort(a);
				tlow = (char) a[2];
				thigh = (char) a[3];
				if (a[2] != a[3]) {
					cc.add(new CharRange((char) (a[1]), (char) (a[2])));
					tlow = (char) (a[2] + 1);
				} else {
					tlow = (char) (a[1]);
				}
				if (a[1] != a[0])
					cc.add(new CharRange((char) (a[0]), (char) (a[1] - 1)));
			}
		}
		return cc.add(new CharRange(tlow, thigh));
	}

	// For '^';
	void negate() {
		int nextlo = 0;
		TreeSet<CharRange> cct = new TreeSet<CharRange>();
		for (CharRange cr : cc) {
			if (cr.low == 0) {
				nextlo = cr.high + 1;
			} else {
				cct.add(new CharRange((char) (nextlo), (char) (cr.low - 1)));
				nextlo = cr.high + 1;
			}
		}
		if (nextlo < Character.MAX_VALUE) {
			cct.add(new CharRange((char) (nextlo), Character.MAX_VALUE));
		}
		cc.clear();
		cc.addAll(cct);
	}

	public void clear() {
		cc.clear();
	}

	@Override
	public String toString() {
		return cc.toString();
	}

	@Override
	public int compareTo(CharacterClass o) {
		if (o.cc.size() == this.cc.size()) {
			Iterator<CharRange> ito = o.cc.iterator();
			Iterator<CharRange> ithis = this.cc.iterator();
			while (ito.hasNext()) {
				CharRange a = ito.next();
				CharRange b = ithis.next();
				if (a.low != b.low)
					return a.low - b.low;
				else if (a.high != b.high)
					return a.high - b.high;
			}
		} else
			return this.cc.size() - o.cc.size();
		return 0;
	}
}

class CharRange implements Comparable<CharRange> {
	char low;
	char high;

	public CharRange(char low, char high) {
		this.low = low;
		this.high = high;
	}

	public char getLow() {
		return this.low;
	}

	public char getHigh() {
		return this.high;
	}

	public CharRange set(char low, char high) {
		this.low = low;
		this.high = high;
		return this;
	}

	@Override
	public int compareTo(CharRange o) {
		// treats this == o if they overlap at all
		if (o.low <= this.low && this.low <= o.high) {
			return 0;
		}
		return this.low - o.high;
	}

	@Override
	public String toString() {
		String slow;
		String shigh;
		if (low >= 0x20 && low <= 0x7E) {
			slow = Character.toString(low);
		} else {
			slow = new String("\\u" + Integer.toHexString(low));
		}

		if (high >= 0x20 && high <= 0x7E) {
			shigh = Character.toString(high);
		} else {
			shigh = new String("\\u" + Integer.toHexString(high));
		}
		if (low == high)
			return slow;
		else
			return new String(slow + "-" + shigh);
	}
}